////////////////////////////////////////////////////////////////////////////
///
/// @file perf_test.c
///
/// @brief
///		Performance test of memory allocator
///
/// @author
///		Anyav
///
/////////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "memalloc.h"
#include "../logger/logger.h"

#define _LIN_
#if 0 /* tmp, to debug test */

#define _MAC_
int ma_init(size_t mem, off_t swap, const char* swap_path){ return 0; }
void ma_deinit(){}
size_t ma_alloc(size_t sz){ return 0; }
int ma_free(size_t id){ return 0; }
void* ma_get(size_t id){ static unsigned int t; return &t; }
int ma_release(size_t id){ return 0; }

#endif /* tmp, to debug test */

#define MS_MULT 1000000
#define SIZE_1MB (off_t)(1LL<<20LL)
#define min(a,b) ( (a) > (b)?(b):(a) )
#define max(a,b) ( (a) > (b)?(a):(b) )

#ifdef _WIN_

#include <windows.h>
typedef unsigned __int64   ULONG64;
ULONG64 os_time_ms()
{
	static LARGE_INTEGER freq = { 0, };
	
	LARGE_INTEGER t;
	if( freq.QuadPart == 0 )
	{
		if( !QueryPerformanceFrequency(&freq) )
		{
			printf("Error_time: queryperformancefreq %x\n", GetLastError() );
			return 0;
		}
	}
	if( !QueryPerformanceCounter(&t) )
	{
		printf("Error_time:QueryPerformanceCounter %x\n", GetLastError() );
		return 0;
	}
	
	return (MS_MULT * t.QuadPart)/freq.QuadPart;
}

#elif defined(_LIN_) || defined(_MAC_)

#include <errno.h>
#include <sys/time.h>
typedef unsigned long long ULONG64;
ULONG64 os_time_ms()
{
	struct timeval t;
	if( 0 != gettimeofday(&t, NULL) )
	{
		printf("Error_time: gettimeofday %x (%s)\n", errno, strerror(errno) );
		return 0;
	}
	
	return (t.tv_sec * MS_MULT + t.tv_usec);
}

#else

#error "Unknown OS type. please use define _LIN_ or _WIN_"

#endif


#define TEST_WRAPPER_START(test_no,test_name)					    \
do{										\
	printf("-----> TEST #" test_no "." test_name ",then free\n" );  \
	start = os_time_ms();											

#define TEST_WRAPPER_END(test_no)									\
	stop = os_time_ms();											\
	printf("-----> TEST #" test_no "time = %llu\n", stop - start );	\
}while(0)

#define SEQ_EMPTY_TEST(test_no,test_name,cnt,alloc_sz) \
	TEST_WRAPPER_START(test_no,test_name)			\
	for( i = 0; i < cnt; i++ )				\
		ids[ i ] = ma_alloc( alloc_sz );		\
	for( i = 0; i < cnt; i++ )				\
		ma_free( ids[ i ] );				\
	TEST_WRAPPER_END(test_no)


#define SEQ_TA4_TEST(test_no,test_name,cnt,alloc_sz) \
	TEST_WRAPPER_START(test_no,test_name)			\
	for( i = 0; i < cnt; i++ )						    \
		ids[ i ] = ma_alloc( alloc_sz );			   	\
	for( i = (cnt)-1; i >= 0; i-- )					    \
		ma_get( ids[ i ] );			    			    \
	for( i = 0; i < cnt; i++ )						    \
		ma_release( ids[ i ] );							\
	for( i = 0; i < cnt; i++ )				            \
		ma_free( ids[ i ] );							\
	TEST_WRAPPER_END(test_no)


#define TA4_TEST(test_no,test_name,cnt,alloc_sz,ta4_cnt) \
TEST_WRAPPER_START(test_no,test_name)					\
															\
	for( i = 0; i < cnt; i++ )							    \
		ids[ i ] = ma_alloc( alloc_sz );					\
	for( i = 0; i < ta4_cnt; i++ )							\
		*(char*)ma_get( ids[ i ] ) = 0xba;					\
	for( i = 0; i < ta4_cnt; i++ )							\
		ma_release( ids[ i ] );								\
	for( i = ta4_cnt; i < min(ta4_cnt*2,cnt); i++ )			\
		ma_get( ids[ i ] );									\
	for( i = ta4_cnt; i < min(ta4_cnt*2,cnt); i++ )			\
		ma_release( ids[ i ] );								\
	for( i = cnt-1; i >= 0; i-- )						    \
		ma_free( ids[ i ] );								\
															\
TEST_WRAPPER_END(test_no)



int main()
{
	LOGGER_START(Logger::Priority::INFO, "log.out");
	ULONG64 start, stop;
	int i;
	
	#define ID_MAX (30*SIZE_1MB)
	size_t* ids = (size_t*)malloc( sizeof(size_t)* ID_MAX );
	
	printf("******** TEST #1. SWAP == HEAP **********\n");
	ma_init( 1800 * SIZE_1MB, 1800 * SIZE_1MB, "swap1.tmp" );
	
	SEQ_EMPTY_TEST( "1.1", "Alloc 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	SEQ_EMPTY_TEST( "1.1", "Alloc 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	SEQ_EMPTY_TEST( "1.1", "Alloc 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	
	SEQ_EMPTY_TEST( "1.2", "Alloc 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_EMPTY_TEST( "1.2", "Alloc 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_EMPTY_TEST( "1.2", "Alloc 64b sz chunks", SIZE_1MB/4, 64 );
	
	SEQ_TA4_TEST( "1.3", "Alloc & touch 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	SEQ_TA4_TEST( "1.3", "Alloc & touch 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	SEQ_TA4_TEST( "1.3", "Alloc & touch 1 to 256KB sz chunks", SIZE_1MB/4, (max(i%4096,64)) );
	
	SEQ_TA4_TEST( "1.4", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_TA4_TEST( "1.4", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_TA4_TEST( "1.4", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	
	ma_deinit();
	
	printf("******** TEST #2. SWAP > HEAP **********\n");
	ma_init( 1024 * SIZE_1MB, 8192 * SIZE_1MB, "swap2.tmp" );
	
	SEQ_TA4_TEST( "2.1", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_TA4_TEST( "2.1", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	SEQ_TA4_TEST( "2.1", "Alloc & touch 64b sz chunks", SIZE_1MB/4, 64 );
	
	TA4_TEST( "2.2", "Alloc & touch part of 64b sz chunks", 2*SIZE_1MB, 64, SIZE_1MB/4 );
	TA4_TEST( "2.2", "Alloc & touch part of 64b sz chunks", 2*SIZE_1MB, 64, SIZE_1MB/4 );
	TA4_TEST( "2.2", "Alloc & touch part of 64b sz chunks", 2*SIZE_1MB, 64, SIZE_1MB/4 );
	
	TA4_TEST( "2.3", "Alloc & touch part of 513b sz chunks", 2*SIZE_1MB, 513, 256 );
	TA4_TEST( "2.3", "Alloc & touch part of 513b sz chunks", 2*SIZE_1MB, 513, 256 );
	TA4_TEST( "2.3", "Alloc & touch part of 513b sz chunks", 2*SIZE_1MB, 513, 256 );
	
	ma_deinit();
	
	return 0;
	
}


